package psn.zjk.exporter;

import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
import psn.zjk.exporter.utils.FileUtils;

import java.io.*;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author sdyt_zhangjunkai
 * @version 1.0
 * @CLASS_NAME ExcelSheetBuilder
 * @DESCRIPTION 不支持大数据表
 * @CREATE_TIME 2020/8/21 14:17
 */
public class ExcelSheetBuilder {
    private static final Map<String, ExcelSheetBuilder> CACHE = new HashMap<>(16);
    XSSFWorkbook workbook;
    XSSFSheet sheet;
    CellListener cellListener;

    private ExcelData excelData;
    private boolean built;

    private ExcelSheetBuilder() {
        workbook = new XSSFWorkbook();
        sheet = workbook.createSheet();
    }

    private ExcelSheetBuilder(InputStream inputStream) throws IOException {
        workbook = new XSSFWorkbook(inputStream);
        int numberOfSheets = workbook.getNumberOfSheets();
        if (numberOfSheets == 0) {
            sheet = workbook.createSheet();
        } else {
            sheet = workbook.getSheetAt(0);
        }
    }

    private static ExcelSheetBuilder newBuilder(InputStream inputStream) throws IOException {
        return new ExcelSheetBuilder(inputStream);
    }

    public static ExcelSheetBuilder newBuilder()  {
        return new ExcelSheetBuilder();
    }

    public static ExcelSheetBuilder newBuilder(String path, String fileName) throws IOException {
        File file = new File(path, fileName);
        FileUtils.createIfNotExist(file);
        return newBuilder(file);
    }

    public static ExcelSheetBuilder newBuilder(File file) throws IOException {
        return new ExcelSheetBuilder(new FileInputStream(file));
    }


    public ExcelSheetBuilder autoSizeColumn(int index) {
        sheet.autoSizeColumn(index);
        return this;
    }


    public ExcelSheetBuilder setData(ExcelData excelData) {
        built = false;
        this.excelData = excelData;
//        Map<Integer, SheetColumn> sheetColumns = excelData.getSheetColumns();
//        Map<Integer, SheetRow> sheetRows = excelData.getSheetRows();
//        for (int i = 0; i < sheetRows.size(); i++) {
//            for (int j = 0; j < sheetColumns.size(); j++) {
//                cell(i, j);
//            }
//        }
        return this;
    }

    private void setCell(SheetCell sheetCell) {
        //前置处理
        if (cellListener != null) {
            cellListener.beforeSet(sheetCell);
        }

        SheetRow sheetRow = sheetCell.getSheetRow();
        SheetColumn sheetColumn = sheetCell.getSheetColumn();

        int rowIndex = sheetRow.getIndex();
        int colIndex = sheetColumn.getIndex();
        XSSFCell cell = cell(rowIndex, colIndex);
        XSSFCellStyle cellStyle = cell.getCellStyle();
        CTXf coreXf = cellStyle.getCoreXf();
        //模板中有字体和边框样式的不改变
        if (cellStyle == null || ((coreXf = cellStyle.getCoreXf()) != null && !coreXf.isSetApplyFont() && !coreXf.isSetApplyBorder())) {
            cellStyle = sheetCell.getStyle();
        }
        fillCell(cell, sheetCell.getValue(), cellStyle);

        //后置处理
        if (cellListener != null) {
            cellListener.afterSet(cell);
        }
    }


    /**
     * @param cell
     * @param data
     * @param style
     * @METHOD_NAME: afterSet
     * @DESCRIPTION: 填充单元格应调用该方法，可触发单元格监听器
     * @CREATE_TIME 2020/9/4 16:41
     * @RETURN void
     * @AUTHOR sdyt_zhangjunkai
     */
    private void fillCell(XSSFCell cell, Object data, XSSFCellStyle style) {
        setCellValue(cell, data);
        if (style == null) {
            cell.setCellStyle(excelData.defaultCellStyle);
        } else {
            cell.setCellStyle(style);
        }
    }

    public ExcelSheetBuilder setCellValue(int rowIndex, int columnIndex, Object value) {
        if (rowIndex >= 0 && columnIndex >= 0) {
            setCellValue(cell(rowIndex, columnIndex), value);
        } else {
            throw new IllegalArgumentException(MessageFormat.format("Invalid coordinate: [{0},{1}]", rowIndex, columnIndex));
        }
        return this;

    }

    public ExcelSheetBuilder setCellValue(XSSFCell cell, Object value) {
        if (value == null) {
            return this;
        }
        Object tmp = value;
//        String rawValue = cell.getRawValue();
//        if (rawValue != null && rawValue.startsWith("#{") && rawValue.endsWith("}")) {
//            StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext(value);
//            SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
//            tmp = spelExpressionParser.parseRaw("rawValue").getMappedValue();
//        }
        if (tmp instanceof Boolean) {
            cell.setCellValue((boolean) tmp);
        } else if (tmp instanceof Date) {
            cell.setCellValue((Date) tmp);
        } else if (tmp instanceof String) {
            cell.setCellValue((String) tmp);
        } else if (tmp instanceof RichTextString) {
            cell.setCellValue((RichTextString) tmp);
        } else if (tmp instanceof Double || tmp instanceof Float || tmp instanceof Integer) {
            cell.setCellValue(Double.valueOf(value.toString()));
        } else if (tmp instanceof Calendar) {
            cell.setCellValue((Calendar) tmp);
            cell.setCellType(0);
        } else {
            throw new IllegalArgumentException(MessageFormat.format("Type not supported: [{0}]{1}", value.getClass(), value));
        }
        return this;
    }

//    public boolean isPrimitive(Object o) {
//        Class<?> clazz = o.getClass();
//        return ClassUtil.isPrimitive(clazz) || clazz == String.class;
//    }


//    /**
//     * @param cell
//     * @param leftText
//     * @param rightText
//     * @METHOD_NAME: digonalWithText
//     * @DESCRIPTION: 单元格添加左下右上的对角线，并填入文本
//     * @CREATE_TIME 2020/8/24 13:02
//     * @RETURN com.citicbank.fmut.position.base.domain.step.ExcelSheetBuilder
//     * @AUTHOR sdyt_zhangjunkai
//     */
//    public ExcelSheetBuilder drawDiagonalWithText(XSSFCell cell, String leftText, String rightText) {
//        int columnIndex = cell.getColumnIndex();
//        int rowIndex = cell.getRowIndex();
//        XSSFDrawing drawingPatriarch = sheet.createDrawingPatriarch();
//        XSSFSimpleShape digonalLine = drawingPatriarch.createSimpleShape(new XSSFClientAnchor(0, 0, 1023, 255, columnIndex, rowIndex, columnIndex + 1, rowIndex + 1));
//        digonalLine.setShapeType(1);
//        digonalLine.setLineStyleColor(0, 0, 0);
//
//        if (leftText != null) {
//            XSSFTextBox textbox = drawingPatriarch.createTextbox(new XSSFClientAnchor(0, 0, 1023, 255, columnIndex, rowIndex, columnIndex + 1, rowIndex + 1));
//            XSSFRichTextString xssfRichTextString = new XSSFRichTextString(rightText);
//            XSSFFont font = workbook.createFont();
//            font.setFontHeight(7);
//            xssfRichTextString.applyFont(font);
//            textbox.setText(xssfRichTextString);
//            textbox.setTopInset(0);
//            textbox.setLeftInset(35);
//            textbox.setTextHorizontalOverflow(TextHorizontalOverflow.OVERFLOW);
//            textbox.setWordWrap(false);
//        }
//
//        if (rightText != null) {
//            XSSFTextBox textbox2 = drawingPatriarch.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, columnIndex, rowIndex, columnIndex + 1, rowIndex + 1));
//            XSSFRichTextString s = new XSSFRichTextString(leftText);
//            XSSFFont font = workbook.createFont();
//            font.setFontHeight(7);
//            s.applyFont(font);
//            textbox2.setText(s);
//            textbox2.setTopInset(4);
//            textbox2.setLeftInset(1);
//            textbox2.setTextHorizontalOverflow(TextHorizontalOverflow.OVERFLOW);
//            textbox2.setWordWrap(false);
//        }
//
//        return this;
//    }


    private void initTable() {
        int rowSize = excelData.sheetRows.size() - 1;
        int columnSize = excelData.sheetColumns.size() - 1;

        int rowIndex = excelData.firstDataRowIndex;
        int columnIndex = excelData.firstDataColumnIndex;
        for (int i = rowIndex; i < rowSize + rowIndex; i++) {
            for (int j = columnIndex; j < columnIndex + columnSize; j++) {
                cell(i, j);
            }
        }

        for (int j = columnIndex; j < columnIndex + columnSize; j++) {
            cell(excelData.firstRow, j);
        }

        for (int j = rowIndex; j < rowIndex + rowSize; j++) {
            cell(j, excelData.firstColumn);
        }

    }

    /**
     * @param
     * @METHOD_NAME: build
     * @DESCRIPTION: 往poi插入数据
     * @CREATE_TIME 2020/9/9 19:01
     * @RETURN org.apache.poi.xssf.usermodel.XSSFSheet
     * @AUTHOR sdyt_zhangjunkai
     */
    public XSSFSheet build() {
        if (excelData==null||excelData.getDatas()==null||excelData.getDatas().size()==0){
            throw new IllegalStateException("no data filled!");
        }
        if (!built) {
            //表数据
            Map<Long, SheetCell> assembledDatas = excelData.getAssembledDatas();
            initTable();
            for (SheetCell sheetCell : assembledDatas.values()) {
                setCell(sheetCell);
            }
            if (excelData.autoSizeColumn) {
                excelData.getSheetColumns().forEach((index, sheetColumn) -> {
                    sheet.autoSizeColumn(index);
                });
            }
            built = true;
        }
        return sheet;
    }

    public XSSFSheet getSheet() {
        return sheet;
    }

    public XSSFWorkbook getWorkbook() {
        return workbook;
    }

    public XSSFCell cell(int rowIndex, int cellIndex) {
        XSSFRow row = row(rowIndex);
        return cell(row, cellIndex);
    }

    public XSSFCell cell(XSSFRow row, int cellIndex) {
        if (row == null) {
            throw new IllegalArgumentException("insertRow is not created");
        }
        XSSFCell cell = row.getCell(cellIndex);
        if (cell == null) {
            cell = row.createCell(cellIndex);
        }
        return cell;
    }

    public XSSFRow row(int rowIndex) {
        XSSFRow row = sheet.getRow(rowIndex);
        if (row == null) {
            row = sheet.createRow(rowIndex);
        }
        return row;
    }


    public void write(OutputStream outputStream) throws IOException {
        build();
        workbook.write(outputStream);
    }

    public void write(String filename) throws IOException {
        build();
        File file = new File(filename);
        if (!file.exists()) {
            file.createNewFile();
        }
        write(new FileOutputStream(file));
    }

//    /**
//     * @param cellStyle
//     * @METHOD_NAME: unsetDiagonal
//     * @DESCRIPTION: 去除对角线
//     * @CREATE_TIME 2020/9/4 11:11
//     * @RETURN org.apache.poi.xssf.usermodel.XSSFCellStyle
//     * @AUTHOR sdyt_zhangjunkai
//     */
//    public XSSFCellStyle unsetDiagonal(XSSFCellStyle cellStyle) {
//        StylesTable stylesSource = workbook.getStylesSource();
//        XSSFCellStyle xssfCellStyle = (XSSFCellStyle) cellStyle.clone();
//        long fromBorderId = xssfCellStyle.getCoreXf().getBorderId();
//
//        CTBorder ctBorder = stylesSource.getBorderAt((int) fromBorderId).getCTBorder();
//        CTBorder copy = (CTBorder) ctBorder.copy();
//        copy.unsetDiagonal();
//        int i = stylesSource.putBorder(new XSSFCellBorder(copy));
//        xssfCellStyle.getCoreXf().setBorderId(i);
//        return xssfCellStyle;
//    }

    /**
     * @param formatter
     * @METHOD_NAME: setCellListener
     * @DESCRIPTION:
     * @CREATE_TIME 2020/9/4 16:52
     * @RETURN com.citicbank.fmut.web.excel.core.ExcelSheetBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelSheetBuilder setCellListener(CellListener formatter) {
        this.cellListener = formatter;
        return this;
    }


    public int getLastRowIndex() {
        return excelData.firstDataRowIndex + excelData.getSheetRows().size() - 1;
    }

    public XSSFRow getLastRow() {
        return row(getLastRowIndex());
    }

    public ExcelSheetBuilder setColumnStyle(int columnIndex, XSSFCellStyle style) {
        sheet.setDefaultColumnStyle(columnIndex, style);
        return this;
    }

    public CellStyleBuilder newCellStyleBuilder() {
        return new CellStyleBuilder(this);
    }


}
